/** \file    thread.cpp
  * \brief   Object-oriented approach to threads manipulation;
  *			 It is safe to throw exceptions in the thread because they are catched by the class.
  * \date    2000 - 2003
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/mswin/src/thread.cpp,v 1.6 2004/09/18 05:07:26 cscom1r4 Exp $
  */

#include "mswin/thread.h"
#include "mswin/exception.h"
#include "xtd/obj_fun.h"

#include <process.h>
#include <assert.h>
#include <iostream>

namespace mswin {

static unsigned __stdcall start_thread(void * arg)
{
	thread & t = *reinterpret_cast<thread*>(arg);
	t.safe_run();
	return t.is_finished_ok();
}

thread::thread(bool create)
{
	_finished_ok = true;
	_restart_on_error = false;
	_hthread = 0;
	if (create)
		this->create(false);
}

void thread::create(bool resume)
{
	assert(!created());
#ifndef _DEBUG
	unsigned _thread_id;
#endif
	_hthread = (HANDLE)_beginthreadex(0, 0, start_thread, this, 
		CREATE_SUSPENDED, &_thread_id);
	if (resume)
		this->resume();
}

thread::~thread()
{
	if (created()) {
#ifdef _DEBUG
		//deletion of object from other threads while the object is running is prohibited
		if (_thread_id != GetCurrentThreadId())
			assert(is_finished());
#endif
		CloseHandle(_hthread);
	}
}

void thread::suspend() 
{ 
	assert(created());
	SuspendThread(_hthread); 
}

void thread::resume() 
{ 
	assert(created());
	ResumeThread(_hthread); 
}

bool thread::is_finished() 
{ 
	assert(created());
	return WaitForSingleObject(_hthread, 0) == WAIT_OBJECT_0; 
}

bool thread::wait_finish() 
{ 
	assert(created());
	WaitForSingleObject(_hthread, INFINITE); 
	return _finished_ok; 
}

void thread::terminate() 
{ 
	assert(created());
	TerminateThread(_hthread, 1); 
}

void thread::report_error(const char * msg)
{
	std::clog << "\nthread error: " << msg << std::endl;
	OutputDebugString(msg);
}

void thread::safe_run()
{
	while (
		!mswin::catch_all(
			xtd::obj_fun(*this, &thread::run), 
			xtd::obj_fun(*this, &thread::exception_occured))
		&& _restart_on_error);
}

void thread::exception_occured(const char * msg)
{
	_finished_ok = false;
	report_error(msg);
}

#ifdef _DEBUG

typedef struct tagTHREADNAME_INFO
{
   DWORD dwType; // must be 0x1000
   LPCSTR szName; // pointer to name (in user addr space)
   DWORD dwThreadID; // thread ID (-1=caller thread)
   DWORD dwFlags; // reserved for future use, must be zero
} THREADNAME_INFO;

void thread::set_debug_name(const char * name)
{
	assert(created());
   THREADNAME_INFO info;
   info.dwType = 0x1000;
   info.szName = name;
   info.dwThreadID = _thread_id;
   info.dwFlags = 0;

   __try
   {
      RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
   }
   __except(EXCEPTION_CONTINUE_EXECUTION) { }
}

#endif //ifdef _DEBUG

} //namespace mswin
